//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-02-01
//
namespace LargoCommon.Music
{
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using Abstract;
using LargoCommon.Interfaces;
///
/// Musical Linearizer.
///
public class MusicalLinearizer
{
#region Fields
///
/// Melodic tones.
///
private static IList melodicTones;
///
/// Initializes a new instance of the class.
///
/// The given header.
public MusicalLinearizer(MusicalHeader givenHeader) {
this.Header = givenHeader;
this.Parts = new List();
}
#endregion
#region Properties
///
/// Gets or sets the header.
///
///
/// The header.
///
public MusicalHeader Header { get; set; }
///
/// Gets or sets Musical Lines.
///
/// Property description.
public IList Lines { get; set; }
///
/// Gets or sets Musical Lines.
///
/// Property description.
public IList Parts { get; set; }
///
/// Gets or sets the total duration.
///
///
/// The total duration.
///
private long TotalDuration { get; set; }
#endregion
#region Public static
///
/// Line Of Tones.
///
/// Source track.
/// Voice number.
/// Returns value.
public static MusicalLine NextRhythmicLineTrack(MusicalLine sourceTrack, byte voice) {
Contract.Requires(sourceTrack != null);
if (sourceTrack == null || sourceTrack.IsEmpty) {
return null;
}
var track = (MusicalLine)sourceTrack.Clone();
if (!((from tone in sourceTrack.Tones
where tone.ToneType == MusicalToneType.Rhythmic && !tone.IsPause && !tone.IsReady
orderby tone.BarNumber
select tone).FirstOrDefault() is MusicalStrike firstTone)) {
return null;
}
//// Key Number represents Instrument but why is not here LineType = MusicalLineType.Rhythmic?
//// if (track.Channel == (byte)MidiChannel.DrumChannel) {
//// track.Status.GChannel = new GeneralChannel(InstrumentGenus.Melodical, firstTone.Instrument, MidiChannel.DrumChannel);
track.FirstStatus.Instrument = new MusicalInstrument((MidiMelodicInstrument)firstTone.InstrumentNumber);
track.FirstStatus.LineType = MusicalLineType.Rhythmic;
var lastTone = firstTone;
var optimalTone = firstTone;
//// if (optimalTone == null) { return null; }
optimalTone.IsReady = true;
optimalTone.Staff = sourceTrack.LineNumber;
optimalTone.Voice = voice;
track.Tones.Add(optimalTone);
foreach (
var tone in
sourceTrack.Tones.Where(tone => tone.ToneType == MusicalToneType.Rhythmic && !tone.IsPause && !tone.IsReady)) {
optimalTone = (tone.InstrumentNumber == lastTone.InstrumentNumber ? tone : null) as MusicalStrike;
if (optimalTone == null) {
continue;
}
optimalTone.IsReady = true;
optimalTone.Staff = sourceTrack.LineNumber;
optimalTone.Voice = voice;
track.Tones.Add(optimalTone);
lastTone = optimalTone;
}
track.FirstStatus.Staff = sourceTrack.LineNumber;
track.FirstStatus.Voice = voice;
return track;
}
#endregion
#region String representation
/// String representation of the object.
/// Returns value.
public override string ToString() {
return "Musical Linearizer";
}
#endregion
#region Public methods
///
/// Line Of Tones.
///
/// Source track.
/// Voice number.
/// Returns value.
public MusicalLine NextMelodicLineTrack(MusicalLine sourceTrack, byte voice) {
Contract.Requires(sourceTrack != null);
if (sourceTrack == null || sourceTrack.IsEmpty) {
return null;
}
var track = (MusicalLine)sourceTrack.Clone();
melodicTones = sourceTrack.Tones
.Where(tone => tone.ToneType == MusicalToneType.Melodic && !tone.IsPause && !tone.IsReady)
.ToList();
if (!((from tone in melodicTones
orderby tone.BarNumber
select tone).FirstOrDefault() is MusicalTone firstTone)) {
return null;
}
//// Key Number represents Instrument but why is not here LineType = MusicalLineType.Rhythmic?
//// if (track.Channel == (byte)MidiChannel.DrumChannel) {
//// track.Instrument = firstTone.Pitch.MidiKeyNumber();
//// track.LineType = MusicalLineType.Rhythmic; }
var lastTone = firstTone;
var optimalTone = firstTone;
optimalTone.IsReady = true;
optimalTone.Staff = sourceTrack.LineNumber;
optimalTone.Voice = voice;
track.Tones.Add(optimalTone);
while (true) {
optimalTone = FindOptimalTone(lastTone);
if (optimalTone == null) {
break;
}
MusicalStrike.CorrectBadBinding(lastTone, optimalTone);
optimalTone.IsReady = true;
optimalTone.Staff = sourceTrack.LineNumber;
optimalTone.Voice = voice;
track.Tones.Add(optimalTone);
lastTone = optimalTone;
}
track.FirstStatus.Staff = sourceTrack.LineNumber;
track.FirstStatus.Voice = voice;
var completedTones = track.Tones.CollectionWithAddedMissingPauses();
var standardizedTones = completedTones.StandardizeTones(this.Header);
track.SetTones(standardizedTones);
return track;
}
///
/// Transfer Parts To Lines.
///
/// if set to true [skip negligible tracks].
public void TransferPartsToTracks(bool skipNegligibleTracks) { //// MusicalBlock block, block.TotalDuration
this.Lines = new List();
int lineIndex = 0;
foreach (var part in this.Parts) {
// ReSharper disable once LoopCanBePartlyConvertedToQuery
foreach (var track in part.MusicalLines) {
//// this.MusicalParts.Select(part => part.MusicalTracks).SelectMany(partTracks => partTracks)) {
//// Test 2013/12
//// Skip negligible tracks !?
if (skipNegligibleTracks) {
var minimumDuration = this.TotalDuration / 10;
if (track.DurationOfTones < minimumDuration) {
continue;
}
}
track.LineIndex = lineIndex++;
track.FirstStatus.BarNumber = 1; //// 2018/12
track.FirstStatus.Instrument = part.Instrument;
track.MainVoice.Channel = part.Channel;
//// track.Status.GChannel = new GeneralChannel(InstrumentGenus.Melodical, part.Instrument, part.Channel);
var tracks = (List)this.Lines;
tracks.Add(track);
}
}
//// 2016 this.NumberOfRhythmicLines = 0;
//// 2016 this.NumberOfMelodicLines = lineIndex;
}
///
/// Split Lines To Parts.
///
/// The block.
/// If set to true [split multi-voice tracks].
public void SplitTracksToParts(MusicalBlock block, FileSplit splitMultiVoiceTracks) {
const float minimalOccupationForSplit = 1.1f;
const float minimalOccupationForInclusion = 0.05f; //// 2015/01
this.TotalDuration = block.TotalDuration;
this.Parts = new List();
byte voice = 0;
byte staff = 0;
foreach (var sourceTrack in this.Lines) {
if (sourceTrack.LineNumber != staff) {
staff = sourceTrack.LineNumber;
voice = 0;
}
var part = new MusicalPart(block) {
Channel = sourceTrack.MainVoice.Channel,
Instrument = sourceTrack.FirstStatus.Instrument //// 2019/10
};
//// Melodic track
if (sourceTrack.FirstStatus.IsMelodic) {
var q = sourceTrack.QuotientOfOccupation();
if (splitMultiVoiceTracks == FileSplit.Total ||
(splitMultiVoiceTracks == FileSplit.Automatic && q > minimalOccupationForSplit)) {
while (true) {
var track = this.NextMelodicLineTrack(sourceTrack, voice++);
if (track == null) {
break;
}
q = track.QuotientOfOccupation();
if (q > minimalOccupationForInclusion) {
part.MusicalLines.Add(track);
}
}
}
else {
part.MusicalLines.Add(sourceTrack);
}
}
//// Rhythmic track
if (sourceTrack.FirstStatus.IsRhythmic) {
while (true) {
var line = NextRhythmicLineTrack(sourceTrack, voice++);
if (line == null) {
break;
}
part.MusicalLines.Add(line);
}
}
//// If there is more sequence in one part, decrement their loudness !?
//// DecrementLoudness(part);
this.Parts.Add(part);
}
}
#endregion
#region Private static methods
///
/// Finds the optimum tone.
///
/// The last tone.
/// Returns value.
private static MusicalTone FindOptimalTone(MusicalTone lastTone) {
Contract.Requires(lastTone != null);
const int maxPassTones = 12;
MusicalTone optimalTone = null;
var optimalValue = 0;
var countPassed = 0;
foreach (var musicalStrike in melodicTones) {
var mtone = (MusicalTone)musicalStrike;
//// .Select(tone => tone as MusicalTone)
var continuous = true;
if (mtone.IsReady) {
continue;
}
var value = 100;
//// Here possibly toleration of some percent of length
var minStartPosition = lastTone.BitPosition + (int)Math.Round(lastTone.Duration * 0.8f); //// 0.9f
if (mtone.BitPosition >= minStartPosition) {
var dist = mtone.Pitch.DistanceFrom(lastTone.Pitch);
if (dist == 0) {
value += 10;
}
else {
value -= dist;
if (dist > DefaultValue.HarmonicOrder) {
value -= 1;
}
}
var bitDistance = Math.Abs(mtone.BitPosition - lastTone.BitPosition);
value -= bitDistance / mtone.BitRange.Order;
if (mtone.BitRange.Length == mtone.BitRange.Order && lastTone.BitRange.Length == lastTone.BitRange.Order) {
value += 1;
}
if (lastTone.BarNumber == mtone.BarNumber - 1 && lastTone.IsGoingToNextBar != mtone.IsFromPreviousBar) {
value -= 100;
continuous = false;
}
if (value > optimalValue && (!continuous || optimalTone == null || mtone.BitPosition <= optimalTone.BitPosition)) { //// || optimalValue < -500
optimalValue = value;
optimalTone = mtone;
}
countPassed++;
}
if (countPassed > maxPassTones) {
break;
}
}
return optimalTone;
}
#endregion
}
}